#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include <sys/dirent.h>
#include "esp_rom_md5.h"
#include "esp_heap_caps.h"
#include "esp_log.h"
#include "file_tool.h"

#define TAG "file.tools"
#define log_e(...) ESP_LOGE(TAG, __VA_ARGS__)
#define log_i(...) ESP_LOGI(TAG, __VA_ARGS__)
#define log_w(...) ESP_LOGW(TAG, __VA_ARGS__)
#define elog_raw printf

#ifdef CONFIG_SPIRAM
#define _malloc_(size) heap_caps_malloc(size, MALLOC_CAP_SPIRAM)
#else
#define _malloc_(size) malloc(size)
#endif

#define SD_MAGIC 0x10090126

static char *mount_list[MOUNT_LIST_MAX] = {0};
static uint8_t mount_list_len = 0;
/**
 *      将数据转换成hex字符串(静态字符串缓冲区, 不需要free)
 *      最多将16个byte数据转成字符串
 *      buffer  : 数据缓冲区
 *      len     : 缓冲区长度
 *      成功返回 静态 字符串指针
 *      失败返回 0
 */
char *get_hex_str(const uint8_t *buffer, uint8_t len)
{
    static char hash_print[32 + 1] = {0};
    hash_print[len * 2] = 0;
    for (int i = 0; i < len; ++i)
    {
        sprintf(&hash_print[i * 2], "%02x", buffer[i]);
    }
    return hash_print;
}

uint8_t *file_get_md5(char *path)
{
    static uint8_t md5[16];
    FILE *fd = fopen(path, "r");
    if (!fd)
    {
        log_e("Failed to open file %s", path);
        return 0;
    }
    //  get file size
    fseek(fd, 0L, SEEK_END);
    size_t length = ftell(fd);
    fseek(fd, 0L, SEEK_SET);
    uint32_t buffer_size = heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT);
    if (buffer_size > 4096)
        buffer_size = 4096;
    size_t read_size = 0;
    void *buffer = _malloc_(buffer_size);
    if (!buffer)
    {
        fclose(fd);
        log_e("Failed to malloc buffer(%ld)", buffer_size);
        return 0;
    }
    md5_context_t context;
    esp_rom_md5_init(&context);
    do
    {
        read_size = fread(buffer, 1, buffer_size, fd);
        if (read_size > 0)
        {
            esp_rom_md5_update(&context, (unsigned char *)buffer, read_size);
            length -= read_size;
        }
    } while (length && read_size > 0);
    free(buffer);
    fclose(fd);
    esp_rom_md5_final(md5, &context);
    return md5;
}

char *file_get_md5_str(char *path)
{
    static char file_md5[33] = {0};
    uint8_t *md5 = file_get_md5(path);
    if (md5)
    {
        strncpy(file_md5, get_hex_str(md5, 16), sizeof(file_md5));
        return file_md5;
    }
    return 0;
}

bool file_exist(const char *path)
{
    if (!path)
        return false;
    FILE *fd = fopen(path, "r");
    if (fd)
    {
        fclose(fd);
        return true;
    }
    return false;
}

bool dir_exist(const char *path)
{
    if (!path || path[0] != '/')
        return false;
    if (path[1] == '\0') /* "/" */
        return true;
    DIR *dir = opendir(path);
    if (dir)
    {
        closedir(dir);
        return true;
    }
    return false;
}

bool mkdirs(char *muldir)
{
    if (!muldir)
        return false;
    int len = strlen(muldir);
    for (int i = 1; i < len; i++)
    {
        if (muldir[i] == '/')
        {
            muldir[i] = '\0';
            if (!dir_exist(muldir))
            {
                elog_raw("[mount] create dir %s\n", muldir);
                mkdir(muldir, 0777);
            }
            muldir[i] = '/';
        }
    }
    if (len > 0 && !dir_exist(muldir))
    {
        elog_raw("[mount] create dir %s\n", muldir);
        mkdir(muldir, 0777);
    }
    return true;
}

bool list_files(char *src_dir, int max_deepin)
{
    if (max_deepin < 0)
        return false;
    if (!strcmp("/", src_dir))
    {
        for (uint8_t i = 0; i < MOUNT_LIST_MAX; i++)
        {
            if (!mount_list[i] || !strlen(mount_list[i]))
                continue;
            elog_raw("%s\n", &mount_list[i][1]);
        }
        return true;
    }
    struct dirent *entry;
    // 1,打开目录操作
    DIR *dirp = opendir(src_dir);
    if (!dirp)
    {
        elog_raw("Not find dir : %s\n", src_dir);
        return false;
    }
    uint16_t path_len = strlen(src_dir);
    // 3,取目录操作
    while (1)
    {
        entry = readdir(dirp);
        if (entry == NULL)
            break;
        // 跳过 . 和 .. 目录
        if (!strncmp(entry->d_name, ".", 1) || !strncmp(entry->d_name, "..", 2))
            continue;
        // 递归目录
        if (entry->d_type == DT_DIR && max_deepin)
        {
            src_dir[path_len] = '/';
            strcpy(&src_dir[path_len + 1], entry->d_name);
            list_files(src_dir, max_deepin - 1);
            src_dir[path_len] = '\0';
        }
        if (entry->d_type == DT_REG)
        {
            elog_raw("%s/%s\n", src_dir, entry->d_name);
        }
    }
    closedir(dirp);
    return true;
}

uint32_t list_files_to_cb(char *path, files_to_cb_t cb, void *user_data)
{
    if (!mount_list_len || !path || !cb)
        return 0;
    uint16_t i = 0;
    uint32_t send_len = 0;
    uint8_t buff[320] = {0};
    /* 根目录获取独立处理 */
    if (!strcmp("/", path))
    {
        buff[0] = '[';
        send_len += cb(buff, 1, user_data);
        for (i = 0; i < MOUNT_LIST_MAX; i++)
        {
            if (!mount_list[i] || !strlen(mount_list[i]))
                continue;
            if (i != 0)
            {
                buff[0] = ',';
                send_len += cb(buff, 1, user_data);
            }
            sprintf((char *)buff, "{\"name\":\"%s\",\"type\":0}", &mount_list[i][1]);
            send_len += cb(buff, strlen((char *)buff), user_data);
        }
        buff[0] = ']';
        send_len += cb(buff, 1, user_data);
        return send_len;
    }
    /* 获取其它目录内容 使用以下代码 */
    struct dirent *entry;
    // 1,打开目录操作
    DIR *dirp = opendir(path);
    if (!dirp)
        return 0;
    uint32_t size = 0;
    uint16_t path_len = strlen(path);
    FILE *fd;
    buff[0] = '[';
    send_len += cb(buff, 1, user_data);
    // 3,取目录操作
    while (1)
    {
        entry = readdir(dirp);
        if (entry == NULL)
            break;
        if (entry->d_type == DT_DIR || entry->d_type == DT_REG)
        {
            if (send_len > 1)
            {
                buff[0] = ',';
                send_len += cb(buff, 1, user_data);
            }
            buff[0] = '{';
            send_len += cb(buff, 1, user_data);

            sprintf((char *)buff, "\"name\":\"%s\",", entry->d_name);
            send_len += cb(buff, strlen((char *)buff), user_data);

            sprintf((char *)buff, "\"type\":%d", entry->d_type == DT_DIR ? 0 : 1);
            send_len += cb(buff, strlen((char *)buff), user_data);

            if (entry->d_type == DT_REG)
            {
                path[path_len] = '/';
                strcpy(&path[path_len + 1], entry->d_name);
                fd = fopen(path, "r");
                if (fd)
                {
                    fseek(fd, 0, SEEK_END);
                    size = ftell(fd);
                    sprintf((char *)buff, ",\"size\":%ld", size); /* 7+len */
                    send_len += cb(buff, strlen((char *)buff), user_data);
                    fclose(fd);
                }
                path[path_len] = '\0';
            }
            buff[0] = '}';
            send_len += cb(buff, 1, user_data);
        }
    }
    buff[0] = ']';
    send_len += cb(buff, 1, user_data);
    closedir(dirp);
    return send_len;
}

uint32_t list_files_to(char *path, char *buffer, unsigned int len)
{
    if (!mount_list_len || !path || !buffer || len < 3)
        return 0;
    char *p = buffer;
    uint16_t i = 0;
    /* 根目录获取独立处理 */
    if (!strcmp("/", path))
    {
        *p++ = '[';
        for (i = 0; i < MOUNT_LIST_MAX; i++)
        {
            if (!mount_list[i] || !strlen(mount_list[i]))
                continue;
            if ((p - buffer + 20 + strlen(mount_list[i + 1])) >= len - 2)
                break;
            if (p - buffer > 10)
                *p++ = ',';
            sprintf(p, "{\"name\":\"%s\",\"type\":0}", &mount_list[i][1]); /* 11+str */
            p += strlen(p);
        }
        *p++ = ']';
        *p = '\0';
        elog_raw("%s\n", buffer);
        return p - buffer;
    }
    /* 获取其它目录内容 使用以下代码 */
    struct dirent *entry;
    // 1,打开目录操作
    DIR *dirp = opendir(path);
    if (!dirp)
        return 0;
    uint32_t size = 0;
    FILE *fd;
    *p++ = '[';
    uint16_t path_len = strlen(path);
    // 3,取目录操作
    while (1)
    {
        entry = readdir(dirp);
        if (entry == NULL)
            break;
        if (entry->d_type == DT_DIR || entry->d_type == DT_REG)
        {
            i = p - buffer + 22 + strlen(entry->d_name);
            if (entry->d_type == DT_DIR)
            {
                if (i > len)
                    break;
            }
            else
            {
                i += 15; /* 7+8 max 99999999 95MB*/
                if (i > len)
                    break;
            }
            *p++ = '{';                                    /* 3 */
            sprintf(p, "\"name\":\"%s\",", entry->d_name); /* 10+str */
            i = strlen(p);
            p += i;
            sprintf(p, "\"type\":%d", entry->d_type == DT_DIR ? 0 : 1); /* 8 */
            i = strlen(p);
            p += i;
            if (entry->d_type == DT_REG)
            {
                path[path_len] = '/';
                strcpy(&path[path_len + 1], entry->d_name);
                fd = fopen(path, "r");
                if (fd)
                {
                    fseek(fd, 0, SEEK_END);
                    size = ftell(fd);
                    elog_raw("%s:%ld\n", entry->d_name, size);
                    sprintf(p, ",\"size\":%ld", size); /* 7+len */
                    i = strlen(p);
                    p += i;
                    fclose(fd);
                }
                path[path_len] = '\0';
            }
            *p++ = '}';
            *p++ = ',';
        }
    }
    if ((p - buffer) > 1)
        *(p - 1) = ']';
    else
        *p++ = ']';
    *p = '\0';
    closedir(dirp);
    return p - buffer;
}

uint32_t list_files_to_buffer(char *path, char *buffer, unsigned int len)
{
    if (!mount_list_len || !path || !buffer)
        return 0;
#define SPACE_MAX 16
    uint8_t space_len = 0;
    /* 根目录获取独立处理 */
    if (!strcmp("/", path))
    {
        for (uint8_t i = 0; i < MOUNT_LIST_MAX; i++)
        {
            if (!mount_list[i] || !strlen(mount_list[i]))
                continue;
            if (i % 6 == 0)
                strcat(buffer, "\n");
            strcat(buffer, &mount_list[i][1]);
            space_len = SPACE_MAX - strlen(&mount_list[i][1]);
            if (space_len > SPACE_MAX)
                space_len = 0;
            while (space_len--)
                strcat(buffer, " ");
        }
        // strcat(buffer, "\n");
        return strlen(buffer);
    }
    /* 获取其它目录内容 使用以下代码 */
    DIR *dir;
    struct dirent *ptr;
    dir = opendir(path);
    if (dir == NULL)
    {
        return 0;
    }
    else
    {
        uint8_t i = 0;
        while ((ptr = readdir(dir)) != NULL)
        {
            if (i++ % 4 == 0)
                strcat(buffer, "\n");
            if (ptr->d_type == DT_DIR)
                strcat(buffer, "\e[33m");
            strcat(buffer, ptr->d_name);
            if (ptr->d_type == DT_DIR)
                strcat(buffer, "\e[0m");
            if (strlen(ptr->d_name) > 44)
                strcat(buffer, "\t");
            else if (strlen(ptr->d_name) >= 32)
                strcat(buffer, "\t\t");
            else if (strlen(ptr->d_name) >= 20)
                strcat(buffer, "\t\t\t");
            else
                strcat(buffer, "\t\t\t\t");
        }
        closedir(dir);
    }
    strcat(buffer, "\n");
    return strlen(buffer);
}

bool list_dirs(char *src_dir, int max_deepin)
{
    if (!mount_list_len || max_deepin < 0)
        return false;
    struct dirent *entry;
    /* 根目录获取独立处理 */
    if (!strcmp("/", src_dir))
    {
        for (uint8_t i = 0; i < MOUNT_LIST_MAX; i++)
        {
            if (!mount_list[i] || !strlen(mount_list[i]))
                continue;
            elog_raw("\e[34m%s\e[0m\n", &mount_list[i][1]);
        }
        return true;
    }
    // 1,打开目录操作
    DIR *dirp = opendir(src_dir);
    if (!dirp)
    {
        elog_raw("Not find dir : %s\n", src_dir);
        return false;
    }
    // 3,取目录操作
    uint16_t path_len = strlen(src_dir);
    while (1)
    {
        entry = readdir(dirp);
        if (entry == NULL)
            break;
        // 跳过 . 和 .. 目录
        if (!strncmp(entry->d_name, ".", 1) || !strncmp(entry->d_name, "..", 2))
            continue;
        // 递归目录
        if (entry->d_type == DT_DIR)
        {
            if (path_len == 1)
                elog_raw("\e[34m/%s\e[0m\n", entry->d_name);
            else
                elog_raw("\e[34m%s/%s\e[0m\n", src_dir, entry->d_name);
            if (max_deepin)
            {
                src_dir[path_len] = '/';
                strcpy(&src_dir[path_len + 1], entry->d_name);
                list_dirs(src_dir, max_deepin - 1);
                src_dir[path_len] = '\0';
            }
        }
    }
    closedir(dirp);
    return true;
}

bool find_files(char *src_dir, const char **names)
{
    if (!src_dir || !names)
        return false;
    struct dirent *entry;
    // 1,打开目录操作
    DIR *dirp = opendir(src_dir);
    if (!dirp)
    {
        elog_raw("Not find dir : %s\n", src_dir);
        return false;
    }
    uint8_t len = 0, i;
    while (names[len])
        len++;
    // 3,取目录操作
    uint16_t path_len = strlen(src_dir);
    while (1)
    {
        entry = readdir(dirp);
        if (entry == NULL)
            break;
        // 跳过 . 和 .. 目录
        if (!strncmp(entry->d_name, ".", 1) || !strncmp(entry->d_name, "..", 2))
            continue;
        // 递归目录
        if (entry->d_type == DT_DIR)
        {
            if (path_len + strlen(entry->d_name) < 256)
            {
                src_dir[path_len] = '/';
                strcpy(&src_dir[path_len + 1], entry->d_name);
                find_files(src_dir, names);
                src_dir[path_len] = '\0';
            }
        }
        if (entry->d_type == DT_REG)
        {
            for (i = 0; i < len; i++)
            {
                if (!strcmp(entry->d_name, names[i]))
                {
                    elog_raw("Find File %s/%s\n", src_dir, entry->d_name);
                    break;
                }
            }
        }
    }
    closedir(dirp);
    return false;
}
void find_file_free(find_file_t list)
{
    if (!list)
        return;
    find_file_t t;
    while (list)
    {
        t = list->next;
        free(list);
        list = t;
    }
}

static bool check_name_1(char *str, char *str2, uint8_t len)
{
    return strstr(str, str2) > 0;
}

static bool check_name_2(char *str, char *str2, uint8_t len)
{
    return strncmp(str, str2, len) == 0;
}

find_file_t find_files_type_name_get_p(char *src_dir, const char **names, uint8_t type, bool (*check_name_cb)(char *str, char *str2, uint8_t len), find_file_t p)
{
    if (!src_dir)
        return 0;
    struct dirent *entry;
    // 1,打开目录操作
    DIR *dirp = opendir(src_dir);
    if (!dirp)
        return 0;
    find_file_t top = p, end = p;
    uint8_t len = 0, i;
    uint16_t str_len = 0;
    if (names)
    {
        while (names[len])
            len++;
    }
    // 3,取目录操作
    uint16_t path_len = strlen(src_dir);
    while (1)
    {
        entry = readdir(dirp);
        if (entry == NULL)
            break;
        // 跳过 . ..  和 System Volume Information 目录
        if (!strncmp(entry->d_name, ".", 1) || !strncmp(entry->d_name, "..", 2) || !strncmp(entry->d_name, "System Volume Information", 25))
            continue;
        if (entry->d_type == DT_REG && type & DT_REG) /* 文件 */
        {
            for (i = 0; i < len; i++)
            {
                // elog_raw("find_files_get_p : %d %s %s\n", i, entry->d_name, names[i]);
                if (check_name_cb && !check_name_cb(entry->d_name, names[i], strlen(names[i])))
                    continue;
                // elog_raw("find : %s/%s\n", src_dir, entry->d_name);
                str_len = path_len + strlen(entry->d_name) + 2;
                find_file_t file = (find_file_t)_malloc_(sizeof(find_file_s) + str_len);
                if (!file)
                {
                    log_e("Failed to malloc find file object buffer(%d)", sizeof(find_file_s) + str_len);
                    return top;
                }
                memset(file, 0, sizeof(find_file_s) + str_len);
                snprintf(file->path, str_len, "%s/%s", src_dir, entry->d_name);

                // snprintf(file->path, sizeof(file->path), "%s/%s", src_dir, entry->d_name);
                if (!top)
                    top = end = file;
                else
                {
                    file->last = end;
                    end->next = file;
                    end = file;
                }
                // elog_raw("find :(last:%8p) %s(%p)\r\n", file->last, file->path, file);
                break;
            }
        }
        // 递归目录
        if (entry->d_type == DT_DIR && type & DT_DIR) /* 目录 */
        {
            if (type & DT_REG) /* 要求递归目录检索文件 */
            {
                if (path_len + strlen(entry->d_name) < 256)
                {
                    src_dir[path_len] = '/';
                    strcpy(&src_dir[path_len + 1], entry->d_name);
                    find_file_t child_top = find_files_type_get_p(src_dir, names, type, end);
                    src_dir[path_len] = '\0';
                    if (!top)
                        top = end = child_top;
                    if (end)
                    {
                        while (end->next)
                            end = end->next;
                    }
                }
                else
                    return top;
            }
            else
            {
                str_len = path_len + strlen(entry->d_name) + 2;
                find_file_t file = (find_file_t)_malloc_(sizeof(find_file_s) + str_len);
                if (!file)
                    return top;
                memset(file, 0, sizeof(find_file_s) + str_len);
                snprintf(file->path, str_len, "%s/%s", src_dir, entry->d_name);

                // snprintf(file->path, sizeof(file->path), "%s/%s", src_dir, entry->d_name);
                // printf("find %s(%p)\r\n", file->path, file);
                if (!top)
                    top = end = file;
                else
                {
                    file->last = end;
                    end->next = file;
                    end = file;
                }
            }
        }
    }
    closedir(dirp);
    return top;
}

find_file_t find_files_type_get_p(char *src_dir, const char **names, uint8_t type, find_file_t p)
{
    return find_files_type_name_get_p(src_dir, names, type, check_name_1, p);
}

find_file_t find_files_get_p(char *src_dir, const char **names, find_file_t p)
{
    return find_files_type_get_p(src_dir, names, DT_REG | DT_DIR, p);
}

uint32_t find_files_to(char *src_dir, const char **names, char *to, unsigned int to_len)
{
    if (!src_dir || !names || !to)
        return 0;
    struct dirent *entry;
    // 1,打开目录操作
    DIR *dirp = opendir(src_dir);
    if (!dirp)
    {
        elog_raw("Not find dir : %s\n", src_dir);
        return 0;
    }
    char *p = to;
    uint8_t len = 0, i;
    while (names[len])
        len++;
    // 3,取目录操作
    uint16_t path_len = strlen(src_dir);
    while (1)
    {
        entry = readdir(dirp);
        if (entry == NULL)
            break;
        // 跳过 . 和 .. 目录
        if (!strncmp(entry->d_name, ".", 1) || !strncmp(entry->d_name, "..", 2))
            continue;
        for (i = 0; i < len; i++)
        {
            printf("%s %s\n", entry->d_name, names[i]);
            if (strstr(entry->d_name, names[i]))
            {
                if ((p - to) + 32 + strlen(entry->d_name) + path_len > to_len)
                    break;
                if (entry->d_type == DT_REG)
                    p += sprintf(p, "{\"name\":\"%s\",\"type\":1, \"path\":\"%s\"},", entry->d_name, src_dir);
                else
                    p += sprintf(p, "{\"name\":\"%s\",\"type\":0, \"path\":\"%s\"},", entry->d_name, src_dir);
            }
        }
        // 递归目录
        if (entry->d_type == DT_DIR)
        {
            if (path_len + strlen(entry->d_name) < 256)
            {
                src_dir[path_len] = '/';
                strcpy(&src_dir[path_len + 1], entry->d_name);
                p += find_files_to(src_dir, names, p, to_len - (p - to));
                src_dir[path_len] = '\0';
            }
            else
                return p - to;
        }
    }
    closedir(dirp);
    return p - to;
}

uint32_t find_name_to_cb(char *src_dir, const char *name, uint8_t *buff, uint32_t len, files_to_cb_t cb, void *user_data)
{
    if (!src_dir || !name || !buff || !cb)
    {
        elog_raw("Insufficient parameters.\n");
        return 0;
    }
    struct dirent *entry;
    // 1,打开目录操作
    DIR *dirp = opendir(src_dir);
    if (!dirp)
    {
        elog_raw("Not find dir : %s\n", src_dir);
        return 0;
    }
    // 3,取目录操作
    uint16_t path_len = strlen(src_dir);
    while (1)
    {
        entry = readdir(dirp);
        if (entry == NULL)
        {
            elog_raw("[file.find] %s end.\n", src_dir);
            break;
        }
        // 跳过 . 和 .. 目录
        if (!strncmp(entry->d_name, ".", 1) || !strncmp(entry->d_name, "..", 2))
            continue;
        if (strstr(entry->d_name, name))
        {
            if (len)
            {
                buff[0] = ',';
                len += cb(buff, 1, user_data);
            }
            if (entry->d_type == DT_DIR)
                sprintf((char *)buff, "{\"name\":\"%s\",\"type\":0, \"path\":\"%s\"}", entry->d_name, src_dir);
            else if (entry->d_type == DT_REG)
                sprintf((char *)buff, "{\"name\":\"%s\",\"type\":1, \"path\":\"%s\"}", entry->d_name, src_dir);
            len += cb(buff, strlen((char *)buff), user_data);
        }
        // 递归目录
        if (entry->d_type == DT_DIR)
        {
            if (path_len + strlen(entry->d_name) < 256)
            {
                src_dir[path_len] = '/';
                strcpy(&src_dir[path_len + 1], entry->d_name);
                len += find_name_to_cb(src_dir, name, buff, len, cb, user_data);
                src_dir[path_len] = '\0';
            }
            else
                return len;
        }
    }
    closedir(dirp);
    return len;
}

uint32_t find_name_to(char *src_dir, const char *name, char *to, unsigned int to_len)
{
    if (!src_dir || !name || !to)
        return 0;
    struct dirent *entry;
    // 1,打开目录操作
    DIR *dirp = opendir(src_dir);
    if (!dirp)
    {
        elog_raw("Not find dir : %s\n", src_dir);
        return 0;
    }
    char *p = to;
    // 3,取目录操作
    uint16_t path_len = strlen(src_dir);
    while (1)
    {
        entry = readdir(dirp);
        if (entry == NULL)
            break;
        // 跳过 . 和 .. 目录
        if (!strncmp(entry->d_name, ".", 1) || !strncmp(entry->d_name, "..", 2))
            continue;
        if (strstr(entry->d_name, name))
        {
            if ((p - to) + 32 + strlen(entry->d_name) + path_len > to_len)
                break;
            if (entry->d_type == DT_DIR)
                sprintf(p, "{\"name\":\"%s\",\"type\":0, \"path\":\"%s\"},", entry->d_name, src_dir);
            else if (entry->d_type == DT_REG)
                sprintf(p, "{\"name\":\"%s\",\"type\":1, \"path\":\"%s\"},", entry->d_name, src_dir);
            p += strlen(p);
        }
        // 递归目录
        if (entry->d_type == DT_DIR)
        {
            if (path_len + strlen(entry->d_name) < 256)
            {
                src_dir[path_len] = '/';
                strcpy(&src_dir[path_len + 1], entry->d_name);
                p += find_name_to(src_dir, name, p, to_len - (p - to));
                src_dir[path_len] = '\0';
            }
            else
                return p - to;
        }
    }
    closedir(dirp);
    return p - to;
}

void file_tool_del_mount(char *path)
{
    for (uint8_t i = 0; i < MOUNT_LIST_MAX; i++)
    {
        if (!strcmp(mount_list[i], path))
        {
            mount_list[i] = NULL;
            mount_list_len--;
        }
    }
}

void file_tool_add_mount(char *path)
{
    int8_t index = -1;
    for (uint8_t i = 0; i < MOUNT_LIST_MAX; i++)
    {
        if (mount_list[i] == NULL)
            index = i;
        else if (mount_list[i] == path)
            return;
    }
    if (index != -1)
    {
        mount_list[index] = path;
        mount_list_len++;
    }
}
